////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   ReverbManagerEAX.cpp
//  Version:     v1.00
//  Created:     15/8/2005 by Tomas.
//  Compilers:   Visual Studio.NET
//  Description: EAX implementation of a ReverbManager.
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"

#ifdef SOUNDSYSTEM_USE_FMODEX400

#include "ReverbManagerEAX.h"
#include "SoundCVars.h"
#include "IRenderer.h"
#include "IAudioDevice.h"
#include <FmodEx/inc/fmod.hpp>

//////////////////////////////////////////////////////////////////////////
// Initialization
//////////////////////////////////////////////////////////////////////////

const float CReverbManagerEAX::UPDATE_THRESHOLD = FLT_EPSILON;

CReverbManagerEAX::CReverbManagerEAX()
: m_ExResult(FMOD_OK),
	m_pDSPEchoUnit(NULL),
	m_fDelay(-1.f),
	m_fDecayRatio(-1.f),
	m_fDryMix(-1.f),
	m_fWetMix(-1.f)
{
}

CReverbManagerEAX::~CReverbManagerEAX()
{
	RemoveEchoDSP();
}

//////////////////////////////////////////////////////////////////////////
void CReverbManagerEAX::Init(IAudioDevice* pAudioDevice, CSoundSystem* pSoundSystem)
{
	// Call the base class' implementation first
	CReverbManager::Init(pAudioDevice, pSoundSystem);

	assert(pAudioDevice);

	m_pAudioDevice = pAudioDevice;
	
	// Only activate the echo unit if it's desired.
	if (g_SoundCVars.g_nReverbEchoDSP == 1)
	{
		// This will make sure that the unit gets created in the Update() method as soon as the reverb unit is available.
		m_nEchoDSPEnabled = 0;
	}
}

//////////////////////////////////////////////////////////////////////////
bool CReverbManagerEAX::SelectReverb(int nReverbType)
{
	bool bResult = false;

	if (m_pAudioDevice)
	{
		bResult       = true;
		m_bNeedUpdate = true;

		if (nReverbType == REVERB_TYPE_NONE)
		{
			m_ReverbEnable = false;
			m_vecActiveReverbPresetAreas.clear();
			Update(false);
		}
		else
		{
			m_ReverbEnable = true;
		}

		switch (nReverbType) 
		{
		case REVERB_TYPE_NONE:
			{
				// Just bypass the echo unit once the reverb got turned off
				if (m_pDSPEchoUnit)
				{
					m_pDSPEchoUnit->setBypass(true);
				}

				break;
			}
		case REVERB_TYPE_SOFTWARE_LOW:
			{
				if (m_pDSPEchoUnit && g_SoundCVars.g_nReverbEchoDSP == 1)
				{
					m_pDSPEchoUnit->setBypass(false);
				}

				break;
			}
		case REVERB_TYPE_SOFTWARE_HIGH:
			{
				if (m_pDSPEchoUnit && g_SoundCVars.g_nReverbEchoDSP == 1)
				{
					m_pDSPEchoUnit->setBypass(false);
				}

				break;
			}
		default:
			{
				bResult = false;

				break;
			}
		}
	}

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
void CReverbManagerEAX::Reset()
{
	// Set reverb and echo unit to "off" type values.
	CRYSOUND_REVERB_PROPERTIES pPropsOff				        = CRYSOUND_REVERB_PRESET_OFF;
	m_sTempEaxPreset.pReverbPreset->EAX					        = pPropsOff;
	m_sTempEaxPreset.pReverbPreset->EchoDSP.fDelay      = 1.0f;
	m_sTempEaxPreset.pReverbPreset->EchoDSP.fDecayRatio = 0.0f;
	m_sTempEaxPreset.pReverbPreset->EchoDSP.fDryMix     = 0.0f;
	m_sTempEaxPreset.pReverbPreset->EchoDSP.fWetMix     = 0.0f;
	m_sTempEaxPreset.pReverbPreset->sPresetName         = "Off";

	// Reset the echo unit relevant members as well.
	m_fDelay      = -1.0f;
	m_fDecayRatio = -1.0f;
	m_fDryMix     = -1.0f;
	m_fWetMix     = -1.0f;

	if (m_pAudioDevice)
	{
		FMOD::System* const pFMODSystem = static_cast<FMOD::System* const>(m_pAudioDevice->GetSoundLibrary());

		if (pFMODSystem)
		{
			FMOD_REVERB_PROPERTIES oReverbProperties = {0};
			CreateFMODReverbProperties(m_sTempEaxPreset.pReverbPreset->EAX, oReverbProperties);

			FMOD_RESULT const eResult = pFMODSystem->setReverbProperties(&oReverbProperties);
			assert(eResult == FMOD_OK);
		}
	}

	Release();
}

void CReverbManagerEAX::Release()
{
	m_vecActiveReverbPresetAreas.clear();

	// Remove the echo unit in case it's been created.
	RemoveEchoDSP();
}

//////////////////////////////////////////////////////////////////////////
// Information
//////////////////////////////////////////////////////////////////////////

// writes output to screen in debug
void CReverbManagerEAX::DrawInformation(IRenderer* pRenderer, float xpos, float ypos)
{
	//ActiveEaxPresetAreasIter It=m_vecActiveEaxPresetAreas.begin();
	//pRenderer->Draw2dLabel(xpos, ypos, 1.5, fColorGreen, false, "EAX - %s",(*It).sPresetName);
	float fColorGreen[4] ={0.0f, 1.0f, 0.0f, 0.7f};
	float fColorGray[4]	 ={0.3f, 0.3f, 0.3f, 0.7f};
	float fColorTemp[4]	 ={0.0f, 0.0f, 0.0f, 0.7f};
	float fColorBlue[4]	 ={0.4f, 0.4f, 1.0f, 0.7f};
	float fColorRed[4]	 ={1.0f, 0.0f, 0.0f, 0.7f};

	if (!m_ReverbEnable)
	{
		pRenderer->Draw2dLabel(xpos, ypos, 1.1f, fColorGray, false, "Reverb disabled");
		ypos += 11;
		return;
	}

	pRenderer->Draw2dLabel(xpos, ypos, 1.1f, fColorBlue, false, "EAX - %s ", m_sTempEaxPreset.pReverbPreset->sPresetName.c_str());
	ypos += 11;

	ActiveReverbPresetAreasIterConst  IterEnd = m_vecActiveReverbPresetAreas.end();
	for (ActiveReverbPresetAreasIterConst  It=m_vecActiveReverbPresetAreas.begin(); It!=IterEnd; ++It)
	{
		float *pCurColor = fColorGreen;

		if ((*It).bFullEffectWhenInside)
			pCurColor = fColorRed;

		fColorTemp[0] = (*It).fWeight * pCurColor[0] + (1.0f - (*It).fWeight) * fColorGray[0];
		fColorTemp[1] = (*It).fWeight * pCurColor[1] + (1.0f - (*It).fWeight) * fColorGray[1];
		fColorTemp[2] = (*It).fWeight * pCurColor[2] + (1.0f - (*It).fWeight) * fColorGray[2];

		stack_string sTemp;
		sTemp.Format("Reverb - %s", (*It).pReverbPreset->sPresetName.c_str());
		if(g_SoundCVars.g_fReverbDynamic > 0.0f)
			sTemp.Format("%s ReverbReflectionDelay: %f ReverbDelay: %f", sTemp.c_str(), m_sTempEaxPreset.pReverbPreset->EAX.fReflectionsDelay, m_sTempEaxPreset.pReverbPreset->EAX.fReverbDelay);

		if(m_pDSPEchoUnit)
		{
			float fValues[4] = {0.0f};
			m_pDSPEchoUnit->getParameter(FMOD_DSP_ECHO_DELAY,				&fValues[0], NULL, 0);
			m_pDSPEchoUnit->getParameter(FMOD_DSP_ECHO_DECAYRATIO,	&fValues[1], NULL, 0);
			m_pDSPEchoUnit->getParameter(FMOD_DSP_ECHO_DRYMIX,			&fValues[2], NULL, 0);
			m_pDSPEchoUnit->getParameter(FMOD_DSP_ECHO_WETMIX,			&fValues[3], NULL, 0);
			sTemp.Format("%s Echo - Delay: %.2f DecayRatio: %.2f DryMix: %.2f WetMix: %.2f", sTemp.c_str(), fValues[0], fValues[1], fValues[2], fValues[3]);
		}

		pRenderer->Draw2dLabel(xpos, ypos, 1.1f, fColorTemp, false, sTemp.c_str());
		ypos += 11;
	}
}


//////////////////////////////////////////////////////////////////////////
// Management
//////////////////////////////////////////////////////////////////////////

// needs to be called regularly
void CReverbManagerEAX::Update(bool const bInside)
{
	if (m_bNeedUpdate)
	{
		m_bNeedUpdate = false;

		// Update the echo DSP unit first!
		if (m_nEchoDSPEnabled != g_SoundCVars.g_nReverbEchoDSP)
		{
			switch (g_SoundCVars.g_nReverbEchoDSP)
			{
			case 0:
				{
					if (m_pDSPEchoUnit)
					{
						// Turn off the echo unit i.e. bypass it.
						m_ExResult = m_pDSPEchoUnit->setBypass(true);
						assert(m_ExResult == FMOD_OK);
					}

					break;
				}
			case 1:
				{
					if (!m_pDSPEchoUnit)
					{
						CreateEchoDSP();
					}
					else
					{
						// Turn on the echo unit i.e. include it in the process chain again.
						m_ExResult = m_pDSPEchoUnit->setBypass(false);
						assert(m_ExResult == FMOD_OK);
					}

					break;
				}
			}
		}

		// Once this method is called send a new ray cast.
		SendRay();

		if (m_ReverbEnable)
		{
			FUNCTION_PROFILER( GetISystem(),PROFILE_SOUND );

			if (!m_bForceUnderwater && g_SoundCVars.g_nGameReverbManagerPause == 0)
			{
				float SumOfWeight = 0;

				// get sum of weights
				ActiveReverbPresetAreasIterConst  ItEnd = m_vecActiveReverbPresetAreas.end();
				for (ActiveReverbPresetAreasIterConst It=m_vecActiveReverbPresetAreas.begin(); It!=ItEnd; ++It)
				{
					SumOfWeight += (*It).fWeight;
				}

				// Verifying Reverb Values enabled/disables
				bool Checkresults = true;
				m_bInside = bInside;

				// if there is only one Preset just interpolate directly with its weight
				if (m_vecActiveReverbPresetAreas.size()==1)
				{
					ActiveReverbPresetAreasIterConst  It = m_vecActiveReverbPresetAreas.begin();
					m_sTempEaxPreset.fWeight										= (*It).fWeight;
					m_sTempEaxPreset.pReverbPreset->EAX					= (*It).pReverbPreset->EAX;
					m_sTempEaxPreset.pReverbPreset->EchoDSP			= (*It).pReverbPreset->EchoDSP;
					m_sTempEaxPreset.pReverbPreset->sPresetName = (*It).pReverbPreset->sPresetName;
				}
				else
				{
					for (ActiveReverbPresetAreasIterConst It=m_vecActiveReverbPresetAreas.begin(); It!=ItEnd; ++It)
					{
						// Copy first element to the temp preset
						if (It==m_vecActiveReverbPresetAreas.begin())
						{
							m_sTempEaxPreset.fWeight										= (*It).fWeight;
							m_sTempEaxPreset.pReverbPreset->EAX					= (*It).pReverbPreset->EAX;
							m_sTempEaxPreset.pReverbPreset->EchoDSP			= (*It).pReverbPreset->EchoDSP;
							m_sTempEaxPreset.pReverbPreset->sPresetName	= "Reverb_Mix";
						}
						else // start interpolating at the 2nd element
						{
							// only blend if needed
							if ((*It).fWeight > 0) 
							{
								float BlendRatio = 0;
								BlendRatio = (*It).fWeight / SumOfWeight;
								EAX3ListenerInterpolate(&m_sTempEaxPreset.pReverbPreset->EAX, &((*It).pReverbPreset->EAX), BlendRatio, &m_sTempEaxPreset.pReverbPreset->EAX, Checkresults);
								EchoDSPInterpolate(m_sTempEaxPreset.pReverbPreset->EchoDSP, (*It).pReverbPreset->EchoDSP, BlendRatio, m_sTempEaxPreset.pReverbPreset->EchoDSP);
								m_sTempEaxPreset.fWeight = BlendRatio;
								//gEnv->pLog->LogToConsole("setting Weight %f Interpolate", BlendRatio);
							}
						}
					}
				}

				// if the weighted sum of effect has not reached 100% then "fill up" with basic preset
				if (SumOfWeight < 1) 
				{
					EAX3ListenerInterpolate(&(m_bInside ? m_EAXIndoorPreset : m_EAXOutdoorPreset), &m_sTempEaxPreset.pReverbPreset->EAX, SumOfWeight, &m_sTempEaxPreset.pReverbPreset->EAX, Checkresults);
					m_sTempEaxPreset.pReverbPreset->sPresetName = m_bInside ? m_sIndoorMix.c_str() : m_sOutdoorMix.c_str();
				}
			}
			else if (g_SoundCVars.g_nGameReverbManagerPause)
			{
				// Turn the reverb off
				CRYSOUND_REVERB_PROPERTIES pPropsOff				= CRYSOUND_REVERB_PRESET_OFF;
				m_sTempEaxPreset.pReverbPreset->EAX					= pPropsOff;
				m_sTempEaxPreset.pReverbPreset->sPresetName	= "Paused";

				// Also the echo unit if it's included in the chain
				if (m_pDSPEchoUnit)
				{
					m_sTempEaxPreset.pReverbPreset->EchoDSP.fDelay      = 1.0f;
					m_sTempEaxPreset.pReverbPreset->EchoDSP.fDecayRatio = 0.0f;
					m_sTempEaxPreset.pReverbPreset->EchoDSP.fDryMix     = 0.0f;
					m_sTempEaxPreset.pReverbPreset->EchoDSP.fWetMix     = 0.0f;
				}
			}
			else if (m_bForceUnderwater)
			{
				CRYSOUND_REVERB_PROPERTIES pPropsUnderWater = CRYSOUND_REVERB_UNDERWATER;
				m_sTempEaxPreset.pReverbPreset->EAX = pPropsUnderWater;
				m_sTempEaxPreset.pReverbPreset->sPresetName = "UnderWater";
			}

			// TODO reactivate weather airabsorption
			// Add the weather effect
			//m_sTempEaxPreset.EAX.AirAbsorptionHF = m_fWeatherAirAbsorptionMultiplyer*m_sTempEaxPreset.EAX.AirAbsorptionHF;
			// TODO take the Weather Inversion into effect
			if (m_pAudioDevice)
			{
				if (g_SoundCVars.g_fReverbDynamic > 0.0f)
				{
					// Determine the shortest distance
					float fShortestDistance = FLT_MAX;
					for (unsigned int i = 0; i < DIRECTIONS_COUNT; ++i)
						if (m_fObstructionTestResults[i] < fShortestDistance)
							fShortestDistance	= m_fObstructionTestResults[i];

					if (fShortestDistance > 0.0f && fShortestDistance < FLT_MAX)
					{
						// If the shortest distance is within the max value for ReverbReflection (0.3 seconds)
						// set the new reflection value accordingly
						float fNewReflectionValue = SONIC_SPEED_METER_PER_SEC * 0.15f;
						if (fShortestDistance <= fNewReflectionValue)
						{
							fNewReflectionValue = ((fShortestDistance / fNewReflectionValue) * 0.3f) * g_SoundCVars.g_fReverbDynamic;
							m_sTempEaxPreset.pReverbPreset->EAX.fReflectionsDelay = min(fNewReflectionValue, 0.3f);
						}
						else
						{
							m_sTempEaxPreset.pReverbPreset->EAX.fReflectionsDelay	= 0.3f; // Max
						}

						//m_sTempEaxPreset.pReverbPreset->EAX.fReverbDelay =; // TODO: Implement this!	
					}
					else
					{
						m_sTempEaxPreset.pReverbPreset->EAX.fReflectionsDelay = 0.3f; // Max
					}
				}

				// Overrides the early and late reverb delays in case the user supplied custom values
				float const fReverbDelay           = g_SoundCVars.g_fReverbDelay;
				float const fReverbReflectionDelay = g_SoundCVars.g_fReverbReflectionDelay;

				if (fReverbDelay >= 0.0f)
				{
					m_sTempEaxPreset.pReverbPreset->EAX.fReverbDelay = fReverbDelay;
				}
				if (fReverbReflectionDelay >= 0.0f)
				{
					m_sTempEaxPreset.pReverbPreset->EAX.fReflectionsDelay = fReverbReflectionDelay;
				}

				FMOD::System* const pFMODSystem          = static_cast<FMOD::System* const>(m_pAudioDevice->GetSoundLibrary());
				FMOD_REVERB_PROPERTIES oReverbProperties = {0};
				CreateFMODReverbProperties(m_sTempEaxPreset.pReverbPreset->EAX, oReverbProperties);
				// TODO evaluate if test against old value is cheaper (pEx->getReverbProperties())
				FMOD_RESULT const eResult = pFMODSystem->setReverbProperties(&oReverbProperties);
				assert(eResult == FMOD_OK);

				if (m_pDSPEchoUnit)
				{
					if( fabsf(m_sTempEaxPreset.pReverbPreset->EchoDSP.fDelay - m_fDelay)>UPDATE_THRESHOLD )
					{
						m_ExResult = m_pDSPEchoUnit->setParameter(FMOD_DSP_ECHO_DELAY,m_sTempEaxPreset.pReverbPreset->EchoDSP.fDelay);
						assert(m_ExResult == FMOD_OK);
						m_fDelay = m_sTempEaxPreset.pReverbPreset->EchoDSP.fDelay;
					}

					if( fabsf(m_sTempEaxPreset.pReverbPreset->EchoDSP.fDecayRatio - m_fDecayRatio)>UPDATE_THRESHOLD )
					{
						m_ExResult = m_pDSPEchoUnit->setParameter(FMOD_DSP_ECHO_DECAYRATIO,	m_sTempEaxPreset.pReverbPreset->EchoDSP.fDecayRatio);
						assert(m_ExResult == FMOD_OK);
						m_fDecayRatio = m_sTempEaxPreset.pReverbPreset->EchoDSP.fDecayRatio;
					}

					if( fabsf(m_sTempEaxPreset.pReverbPreset->EchoDSP.fDryMix - m_fDryMix)>UPDATE_THRESHOLD )
					{
						m_ExResult = m_pDSPEchoUnit->setParameter(FMOD_DSP_ECHO_DRYMIX, m_sTempEaxPreset.pReverbPreset->EchoDSP.fDryMix);
						assert(m_ExResult == FMOD_OK);
						m_fDryMix = m_sTempEaxPreset.pReverbPreset->EchoDSP.fDryMix;
					}

					if( fabsf(m_sTempEaxPreset.pReverbPreset->EchoDSP.fWetMix - m_fWetMix)>UPDATE_THRESHOLD )
					{
						m_ExResult = m_pDSPEchoUnit->setParameter(FMOD_DSP_ECHO_WETMIX, m_sTempEaxPreset.pReverbPreset->EchoDSP.fWetMix);
						assert(m_ExResult == FMOD_OK);
						m_fWetMix = m_sTempEaxPreset.pReverbPreset->EchoDSP.fWetMix;
					}
				}
			}
		}
		else
		{
			m_sTempEaxPreset.pReverbPreset->EAX = m_EAXOffPreset;
			m_sTempEaxPreset.pReverbPreset->sPresetName = "Off";

			// Tell FMOD about the new reverb settings.
			FMOD::System* const pFMODSystem = static_cast<FMOD::System* const>(m_pAudioDevice->GetSoundLibrary());

			if (pFMODSystem)
			{
				FMOD_REVERB_PROPERTIES oReverbProperties = {0};
				CreateFMODReverbProperties(m_sTempEaxPreset.pReverbPreset->EAX, oReverbProperties);

				FMOD_RESULT const eResult = pFMODSystem->setReverbProperties(&oReverbProperties);
				assert(eResult == FMOD_OK);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CReverbManagerEAX::CreateEchoDSP()
{
	if(m_pAudioDevice)
	{
		FMOD::System* pFMODSystem = (FMOD::System*)m_pAudioDevice->GetSoundLibrary();
		if(pFMODSystem)
		{
			FMOD::DSP* pDSPHeadUnit = NULL;
			m_ExResult = pFMODSystem->getDSPHead(&pDSPHeadUnit);
			if(pDSPHeadUnit)
			{
				FMOD::DSP* pDSPTargetUnit = NULL;
				m_ExResult = pDSPHeadUnit->getInput(0, &pDSPTargetUnit, NULL);
				if(pDSPTargetUnit)
				{
					int iNumInputs = 0;
					m_ExResult = pDSPTargetUnit->getNumInputs(&iNumInputs);

					for(int i = 0; i < iNumInputs; ++i)
					{
						FMOD::DSP*	pDSPReverbUnit = NULL;
						m_ExResult = pDSPTargetUnit->getInput(i, &pDSPReverbUnit, NULL);

						FMOD_DSP_TYPE eDSPType = FMOD_DSP_TYPE_UNKNOWN;
						if(pDSPReverbUnit)
						{
							m_ExResult = pDSPReverbUnit->getType(&eDSPType);
							if(eDSPType == FMOD_DSP_TYPE_SFXREVERB)
							{
								m_ExResult = pFMODSystem->createDSPByType(FMOD_DSP_TYPE_ECHO, &m_pDSPEchoUnit);
								if(m_ExResult == FMOD_OK)
								{
									m_ExResult = pDSPTargetUnit->disconnectFrom(pDSPReverbUnit);
									assert(m_ExResult == FMOD_OK);
									m_ExResult = m_pDSPEchoUnit->addInput(pDSPReverbUnit, NULL);
									assert(m_ExResult == FMOD_OK);
									m_ExResult = pDSPTargetUnit->addInput(m_pDSPEchoUnit, NULL);
									assert(m_ExResult == FMOD_OK);
									m_ExResult = m_pDSPEchoUnit->setActive(true);
									assert(m_ExResult == FMOD_OK);

									m_nEchoDSPEnabled = 1;
								}
							}
						}
					}
				}
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CReverbManagerEAX::RemoveEchoDSP()
{
	if (m_pAudioDevice && m_pDSPEchoUnit)
	{
		FMOD::System* pFMODSystem = (FMOD::System*)m_pAudioDevice->GetSoundLibrary();

		if (pFMODSystem)
		{
			FMOD::DSP* pDSPHeadUnit = NULL;
			m_ExResult = pFMODSystem->getDSPHead(&pDSPHeadUnit);

			if (pDSPHeadUnit)
			{
				FMOD::DSP* pDSPTargetUnit = NULL;
				m_ExResult = pDSPHeadUnit->getInput(0, &pDSPTargetUnit, NULL);

				if (pDSPTargetUnit)
				{
					int nNumInputs = 0;
					m_ExResult = pDSPTargetUnit->getNumInputs(&nNumInputs);

					for (int i = 0; i < nNumInputs; ++i)
					{
						FMOD::DSP* pDSPEchoUnit = NULL;
						m_ExResult = pDSPTargetUnit->getInput(i, &pDSPEchoUnit, NULL);

						if (pDSPEchoUnit)
						{
							FMOD_DSP_TYPE eDSPType = FMOD_DSP_TYPE_UNKNOWN;
							m_ExResult = pDSPEchoUnit->getType(&eDSPType);

							if (eDSPType == FMOD_DSP_TYPE_ECHO)
							{
								FMOD::DSP* pDSPReverbUnit = NULL;
								m_ExResult = pDSPEchoUnit->getInput(0, &pDSPReverbUnit, NULL);

								if (pDSPReverbUnit)
								{
									m_ExResult = pDSPReverbUnit->getType(&eDSPType);

									if (eDSPType == FMOD_DSP_TYPE_SFXREVERB)
									{
										m_ExResult = pDSPEchoUnit->disconnectFrom(pDSPReverbUnit);
										assert(m_ExResult == FMOD_OK);
										m_ExResult = pDSPTargetUnit->disconnectFrom(pDSPEchoUnit);
										assert(m_ExResult == FMOD_OK);
										m_ExResult = pDSPTargetUnit->addInput(pDSPReverbUnit, NULL);
										assert(m_ExResult == FMOD_OK);

										// Now finally remove it from the event system
										m_ExResult = m_pDSPEchoUnit->remove();
										assert(m_ExResult == FMOD_OK);
										m_ExResult = m_pDSPEchoUnit->release();
										assert(m_ExResult == FMOD_OK);

										m_pDSPEchoUnit    = NULL;
										m_nEchoDSPEnabled = 0;
									}

									break;
								}
							}
						}
					}
				}
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
FMOD::DSP* const CReverbManagerEAX::FindReverbUnit()
{
	// If there's an echo unit already let's just get the first input as it must be the reverb unit
	if (m_pDSPEchoUnit)
	{
		FMOD::DSP* pDSPReverbUnit = NULL;
		m_ExResult = m_pDSPEchoUnit->getInput(0, &pDSPReverbUnit, NULL);
		assert(m_ExResult == FMOD_OK);

		if (pDSPReverbUnit)
		{
			FMOD_DSP_TYPE eDSPType = FMOD_DSP_TYPE_UNKNOWN;
			m_ExResult = pDSPReverbUnit->getType(&eDSPType);
			assert(m_ExResult == FMOD_OK);
			assert(eDSPType == FMOD_DSP_TYPE_SFXREVERB);
			return pDSPReverbUnit;
		}

		// This is bad
		assert(0);
	}

	// Here there is no additional echo unit included in the processing chain
	if (m_pAudioDevice)
	{
		FMOD::System* const pFMODSystem = (FMOD::System* const)m_pAudioDevice->GetSoundLibrary();

		if (pFMODSystem)
		{
			FMOD::DSP* pDSPHeadUnit = NULL;
			m_ExResult = pFMODSystem->getDSPHead(&pDSPHeadUnit);
			assert(m_ExResult == FMOD_OK);

			if (pDSPHeadUnit)
			{
				FMOD::DSP* pDSPTargetUnit = NULL;
				m_ExResult = pDSPHeadUnit->getInput(0, &pDSPTargetUnit, NULL);
				assert(m_ExResult == FMOD_OK);

				if (pDSPTargetUnit)
				{
					int nNumInputs = 0;
					m_ExResult = pDSPTargetUnit->getNumInputs(&nNumInputs);
					assert(m_ExResult == FMOD_OK);

					for (int i = 0; i < nNumInputs; ++i)
					{
						FMOD::DSP* pDSPReverbUnit = NULL;
						m_ExResult = pDSPTargetUnit->getInput(i, &pDSPReverbUnit, NULL);
						assert(m_ExResult == FMOD_OK);

						if (pDSPReverbUnit)
						{
							FMOD_DSP_TYPE eDSPType = FMOD_DSP_TYPE_UNKNOWN;
							m_ExResult = pDSPReverbUnit->getType(&eDSPType);
							assert(m_ExResult == FMOD_OK);

							if (eDSPType == FMOD_DSP_TYPE_SFXREVERB)
								return pDSPReverbUnit;
						}
					}
				}
			}
		}
	}

	return NULL;
}

//////////////////////////////////////////////////////////////////////////
void CReverbManagerEAX::DrawDynamicReverbDebugInfo(IRenderer* const pRenderer)
{
	CReverbManager::DrawDynamicReverbDebugInfo(pRenderer);

	float const fDarkGreen[4] = {0.0f, 0.5f, 0.2f, 1.0f};
	pRenderer->Draw2dLabel(500.0f, 400.0f, 2.0f, fDarkGreen, false, "ReflectionDelay: %f", m_sTempEaxPreset.pReverbPreset->EAX.fReflectionsDelay);
}

//////////////////////////////////////////////////////////////////////////
void CReverbManagerEAX::TogglePause(bool const bPause)
{
	g_SoundCVars.g_nGameReverbManagerPause = bPause ? 1 : 0;
	
	// Update once to make sure the new settings got applied,
	// in case the application does not update the reverb manager afterwards
	m_bNeedUpdate = true;
	Update(false);
}

//////////////////////////////////////////////////////////////////////////
void CReverbManagerEAX::CreateFMODReverbProperties(CRYSOUND_REVERB_PROPERTIES const& rCrySoundReverbProperties, FMOD_REVERB_PROPERTIES& rFMODReverbProperties) const
{
	rFMODReverbProperties.Room             = rCrySoundReverbProperties.nRoom;
	rFMODReverbProperties.RoomHF           = rCrySoundReverbProperties.nRoomHF;
	rFMODReverbProperties.RoomLF           = rCrySoundReverbProperties.nRoomLF;
	rFMODReverbProperties.DecayTime        = rCrySoundReverbProperties.fDecayTime;
	rFMODReverbProperties.DecayHFRatio     = rCrySoundReverbProperties.fDecayHFRatio;
	rFMODReverbProperties.Reflections      = rCrySoundReverbProperties.nReflections;
	rFMODReverbProperties.ReflectionsDelay = rCrySoundReverbProperties.fReflectionsDelay;
	rFMODReverbProperties.Reverb           = rCrySoundReverbProperties.nReverb;
	rFMODReverbProperties.ReverbDelay      = rCrySoundReverbProperties.fReverbDelay;
	rFMODReverbProperties.HFReference      = rCrySoundReverbProperties.fHFReference;
	rFMODReverbProperties.LFReference      = rCrySoundReverbProperties.fLFReference;
	rFMODReverbProperties.Diffusion        = rCrySoundReverbProperties.fDiffusion;
	rFMODReverbProperties.Density          = rCrySoundReverbProperties.fDensity;

	// Deprecated members, to be removed with the introduction of FMOD Studio!!
	rFMODReverbProperties.DecayLFRatio     = 0.0f;
	rFMODReverbProperties.EnvDiffusion     = 0.0f;
	rFMODReverbProperties.Environment      = 0;
	rFMODReverbProperties.Flags            = FMOD_REVERB_FLAGS_DEFAULT;
	rFMODReverbProperties.Instance         = 0;
	rFMODReverbProperties.ModulationDepth  = 0.0f;
	rFMODReverbProperties.ModulationTime   = 0.0f;
}
#endif // SOUNDSYSTEM_USE_FMODEX400
